"""This module holds the classes and methods to ease the drawing integration with opengl"""
import pygame
from math import log, ceil
from OpenGL.GL import *
from OpenGL.GLU import *

def glInit(width, heigth):
    """Inits the OpenGL context to a viewport of the given dimensions"""
    
    from OpenGL.error import ErrorChecker
    ErrorChecker._currentChecker=ErrorChecker.nullGetError
    
    glClearColor(0.0, 0.0, 0.0, 1.0) # color de fondo
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # limpiar el buffer

    glMatrixMode(GL_PROJECTION)
    gluOrtho2D(0, width, 0, heigth) # el area de dibujado va a ser un cubo de (0, 0, 0) a (w, h, 1)
    glMatrixMode(GL_MODELVIEW)

    glDisable(GL_DEPTH_TEST)
    glEnable(GL_BLEND) # yai!! alpha blending
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # como se calcula el color de cada px

def _next_pow2_size(size):
    """returns a tuple with x and y equalts to the the next power of two to
    the greatest of the two given numbers"""
    r = 2**ceil(log(max(size), 2))
    return r, r

def surface_to_texture(surface, non_pow2 = False, use_displaylists=True,
                       pix_format=GL_RGBA, min_filter=GL_LINEAR,
                       mag_filter=GL_LINEAR, n_mipmaps=0):
    if non_pow2:
        dimensions = surface.get_rect().size
        img = surface.copy()
    else:
        dimensions = _next_pow2_size(surface.get_rect().size)
        img = pygame.transform.scale(surface, (int(dimensions[0]), int(dimensions[1])))
    
    data = pygame.image.tostring(img, 'RGBA', 1)
    
    glEnable(GL_TEXTURE_2D) # dibujaremos con texturas
    
    texture = glGenTextures(1)
    
    glBindTexture(GL_TEXTURE_2D, texture)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter)
    glTexImage2D(GL_TEXTURE_2D, # target texture type
                 n_mipmaps, # number of mipmaps
                 pix_format, # texture format
                 dimensions[0], dimensions[1],
                 0, #border
                 GL_RGBA, # input pixel format
                 GL_UNSIGNED_BYTE, # input data format
                 data) # pixel data
        
    glDisable(GL_TEXTURE_2D)
    return texture

class GLSurface:
    """Wraps a textured quad into a usable class"""
    
    def __init__(self, surface, use_displaylists=True, pix_format=GL_RGBA,
                 min_filter=GL_LINEAR, mag_filter=GL_LINEAR, n_mipmaps=0,
                 center=True):
        """surface - A pygame surface with the texture
        use_displaylists - Store the quad operation in a display list
        pix_format - format to store the pixel data of the texture
        min_fiter - Filter to aply when a texel is bigger than a pixel (GL_NEAREST)
        mag_filter - Filter to apply when a pixel is bigger than a texel (GL_LINEAR)
        n_mipmaps - number of mipmas to create (if applicable)
        center - center the quad on (0, 0)"""
        
        # the dimensions of the image remains the same even using pow2 textures
        self.rect = surface.get_rect()
        self.texture = surface_to_texture(surface, False, use_displaylists,
                       pix_format, min_filter, mag_filter, n_mipmaps)
        
        self.center = center
        
        if use_displaylists:
            self.displayList = glGenLists(1)
            glNewList(self.displayList, GL_COMPILE)
            self._draw_quad()
            glEndList()
        else:
            self.displayList = None
    
    def __del__(self):
        if self.texture != None:
            if glDeleteTextures:
                glDeleteTextures(self.texture)
            self.texture = None
        if self.displayList != None:
            if glDeleteLists:
                glDeleteLists(self.displayList, 1)
            self.displayList = None
    
    def _draw_quad(self):
        """Draws the textured quad asociated with this oglsurface"""
        # notice that top and bottom values are inverted, for in opengl, (0, 0)
        # is the bottom left corner
        glBindTexture(GL_TEXTURE_2D, self.texture)
        if self.center:
            glTranslate(-self.rect.w / 2, -self.rect.h / 2, 0)
        glBegin(GL_QUADS)
        glTexCoord2f(0.0, 0.0)
        glVertex2f(self.rect.topleft[0], self.rect.topleft[1])
        glTexCoord2f(0.0, 1.0)
        glVertex2f(self.rect.bottomleft[0], self.rect.bottomleft[1])
        glTexCoord2f(1.0, 1.0)
        glVertex2f(self.rect.bottomright[0], self.rect.bottomright[1])
        glTexCoord2f(1.0, 0.0)
        glVertex2f(self.rect.topright[0], self.rect.topright[1])
        glEnd()
        if self.center:
            glTranslate(self.rect.w / 2, self.rect.h / 2, 0)
    
    def draw(self, position, rotation=None, scale=None, tint=(1, 1, 1, 1)):
        glPushMatrix()

        glTranslate(position[0], position[1], 0)

        if scale:
            if isinstance(scale, tuple):
                glScalef(scale[0], scale[1], 1.0)
            else:
                glScalef(scale, scale, 1.0)

        if rotation:
            glRotate(rotation, 0, 0, 1)

        glColor4fv(tint)

        if self.displayList:
            glCallList(self.displayList)
        else:
            self._draw_quad()
            
        glPopMatrix()
    
    def get_rect(self):
        return pygame.Rect(self.rect)

class GLGroup(pygame.sprite.AbstractGroup):
    
    def clear(self, surface, bgd):
        pass

    def draw(self, viewport = None):
        for s in self:
            if viewport.collidepoint(s.rect.center):
                s.draw()

    def _draw(self, sprite):
        #if not self.viewport or self.viewport.collidepoint(sprite.rect.center):
        sprite.draw()

class GLFont:    
    _charmap = None
    def __init__(self, chars=' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO'
                 + 'PQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~',
                 font = None, size=24):
        self._charmap = {}
        pygame.font.init()
        pgf = pygame.font.Font(font, size)
        self.line_heigth = pgf.get_linesize()
        for i in chars:
            self._charmap[i] = GLSurface(pgf.render(i, 1, (255, 255, 255)),
                                         center=False)

    def render(self, text, pos, rotation=0, tint=(1, 1, 1, 1), zoom=1):
        f = self._charmap
        p = list(pos)
        glPushMatrix()
        glTranslate(p[0], p[1], 0)
        glScalef(zoom, zoom, 1)
        p = [0, 0]
        glRotate(rotation, 0, 0, -1)
        for c in str(text):
            if not f.has_key(c):
                if c == '\n':
                    p[1] -= self.line_heigth
                    p[0] = 0
                    continue
                else:
                    c = '?'
            f[c].draw(p, tint=tint)
            p[0] += f[c].rect.w
        glPopMatrix()

    def length(self, text, zoom=1):
        f = self._charmap
        w = 0
        mw = w
        for c in text:
            if f.has_key(c):
                w += f[c].rect.w
            elif c == '\n':
                if w > mw:
                    mw = w
                w = 0
        if w > mw:
            mw = w
        return mw * zoom
